/*
 * Copyright 2020 Google Inc.
 *
 * Use of this source code is governed by a BSD-style license that can be
 * found in the LICENSE file.
 */

#include "src/gpu/ganesh/GrRecordingContextPriv.h"

#include "include/core/SkColorSpace.h"
#include "src/gpu/ganesh/Device.h"
#include "src/gpu/ganesh/GrCaps.h"
#include "src/gpu/ganesh/GrDrawingManager.h"
#include "src/gpu/ganesh/GrProxyProvider.h"
#include "src/gpu/ganesh/GrRenderTargetProxy.h"
#include "src/gpu/ganesh/GrSurfaceProxyView.h"
#include "src/gpu/ganesh/SurfaceDrawContext.h"
#include "src/gpu/ganesh/SurfaceFillContext.h"

void GrRecordingContextPriv::addOnFlushCallbackObject(GrOnFlushCallbackObject *onFlushCBObject)
{
    this->context()->addOnFlushCallbackObject(onFlushCBObject);
}

sk_sp<skgpu::ganesh::Device> GrRecordingContextPriv::createDevice(GrColorType colorType, sk_sp<GrSurfaceProxy> proxy,
    sk_sp<SkColorSpace> colorSpace, GrSurfaceOrigin origin, const SkSurfaceProps &props,
    skgpu::ganesh::Device::InitContents init)
{
    return skgpu::ganesh::Device::Make(this->context(), colorType, std::move(proxy), std::move(colorSpace), origin,
        props, init);
}

sk_sp<skgpu::ganesh::Device> GrRecordingContextPriv::createDevice(skgpu::Budgeted budgeted, const SkImageInfo &ii,
    SkBackingFit fit, int sampleCount, skgpu::Mipmapped mipmapped, GrProtected isProtected, GrSurfaceOrigin origin,
    const SkSurfaceProps &props, skgpu::ganesh::Device::InitContents init)
{
    return skgpu::ganesh::Device::Make(this->context(), budgeted, ii, fit, sampleCount, mipmapped, isProtected, origin,
        props, init);
}

void GrRecordingContextPriv::moveRenderTasksToDDL(GrDeferredDisplayList *ddl)
{
    this->context()->drawingManager()->moveRenderTasksToDDL(ddl);
}

sktext::gpu::SDFTControl GrRecordingContextPriv::getSDFTControl(bool useSDFTForSmallText) const
{
#if !defined(SK_DISABLE_SDF_TEXT)
    return sktext::gpu::SDFTControl{ this->caps()->shaderCaps()->supportsDistanceFieldText(), useSDFTForSmallText,
        !this->caps()->disablePerspectiveSDFText(), this->options().fMinDistanceFieldFontSize,
        this->options().fGlyphsAsPathsFontSize };
#else
    return sktext::gpu::SDFTControl{};
#endif
}

std::unique_ptr<skgpu::ganesh::SurfaceContext> GrRecordingContextPriv::makeSC(GrSurfaceProxyView readView,
    const GrColorInfo &info)
{
    // It is probably not necessary to check if the context is abandoned here since uses of the
    // SurfaceContext which need the context will mostly likely fail later on w/o an issue.
    // However having this here adds some reassurance in case there is a path that doesn't
    // handle an abandoned context correctly. It also lets us early out of some extra work.
    if (this->context()->abandoned()) {
        return nullptr;
    }
    GrSurfaceProxy *proxy = readView.proxy();
    SkASSERT(proxy && proxy->asTextureProxy());

    std::unique_ptr<skgpu::ganesh::SurfaceContext> sc;
    if (proxy->asRenderTargetProxy()) {
        // Will we ever want a swizzle that is not the default write swizzle for the format and
        // colorType here? If so we will need to manually pass that in.
        skgpu::Swizzle writeSwizzle;
        if (info.colorType() != GrColorType::kUnknown) {
            writeSwizzle = this->caps()->getWriteSwizzle(proxy->backendFormat(), info.colorType());
        }
        GrSurfaceProxyView writeView(readView.refProxy(), readView.origin(), writeSwizzle);
        if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
            sc = std::make_unique<skgpu::ganesh::SurfaceDrawContext>(this->context(), std::move(readView),
                std::move(writeView), info.colorType(), info.refColorSpace(), SkSurfaceProps());
        } else {
            sc = std::make_unique<skgpu::ganesh::SurfaceFillContext>(this->context(), std::move(readView),
                std::move(writeView), info);
        }
    } else {
        sc = std::make_unique<skgpu::ganesh::SurfaceContext>(this->context(), std::move(readView), info);
    }
    SkDEBUGCODE(sc->validate();) return sc;
}

std::unique_ptr<skgpu::ganesh::SurfaceContext> GrRecordingContextPriv::makeSC(const GrImageInfo &info,
    const GrBackendFormat &format, std::string_view label, SkBackingFit fit, GrSurfaceOrigin origin,
    GrRenderable renderable, int sampleCount, skgpu::Mipmapped mipmapped, GrProtected isProtected,
    skgpu::Budgeted budgeted)
{
    SkASSERT(renderable == GrRenderable::kYes || sampleCount == 1);
    if (this->abandoned()) {
        return nullptr;
    }
    sk_sp<GrTextureProxy> proxy = this->proxyProvider()->createProxy(format, info.dimensions(), renderable, sampleCount,
        mipmapped, fit, budgeted, isProtected, label);
    if (!proxy) {
        return nullptr;
    }

    skgpu::Swizzle swizzle;
    if (info.colorType() != GrColorType::kUnknown && !this->caps()->isFormatCompressed(format)) {
        swizzle = this->caps()->getReadSwizzle(format, info.colorType());
    }

    GrSurfaceProxyView view(std::move(proxy), origin, swizzle);
    return this->makeSC(std::move(view), info.colorInfo());
}

std::unique_ptr<skgpu::ganesh::SurfaceFillContext> GrRecordingContextPriv::makeSFC(GrImageInfo info,
    std::string_view label, SkBackingFit fit, int sampleCount, skgpu::Mipmapped mipmapped, GrProtected isProtected,
    GrSurfaceOrigin origin, skgpu::Budgeted budgeted)
{
    if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
        return skgpu::ganesh::SurfaceDrawContext::Make(this->context(), info.colorType(), info.refColorSpace(), fit,
            info.dimensions(), SkSurfaceProps(), label, sampleCount, mipmapped, isProtected, origin, budgeted);
    }
    GrBackendFormat format = this->caps()->getDefaultBackendFormat(info.colorType(), GrRenderable::kYes);
    sk_sp<GrTextureProxy> proxy = this->proxyProvider()->createProxy(format, info.dimensions(), GrRenderable::kYes,
        sampleCount, mipmapped, fit, budgeted, isProtected, label);
    if (!proxy) {
        return nullptr;
    }
    skgpu::Swizzle readSwizzle = this->caps()->getReadSwizzle(format, info.colorType());
    skgpu::Swizzle writeSwizzle = this->caps()->getWriteSwizzle(format, info.colorType());

    GrSurfaceProxyView readView(proxy, origin, readSwizzle);
    GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);
    std::unique_ptr<skgpu::ganesh::SurfaceFillContext> sfc;
    sfc = std::make_unique<skgpu::ganesh::SurfaceFillContext>(this->context(), std::move(readView),
        std::move(writeView), info.colorInfo());
    sfc->discard();
    return sfc;
}

std::unique_ptr<skgpu::ganesh::SurfaceFillContext> GrRecordingContextPriv::makeSFC(SkAlphaType alphaType,
    sk_sp<SkColorSpace> colorSpace, SkISize dimensions, SkBackingFit fit, const GrBackendFormat &format,
    int sampleCount, skgpu::Mipmapped mipmapped, GrProtected isProtected, skgpu::Swizzle readSwizzle,
    skgpu::Swizzle writeSwizzle, GrSurfaceOrigin origin, skgpu::Budgeted budgeted, std::string_view label)
{
    SkASSERT(!dimensions.isEmpty());
    SkASSERT(sampleCount >= 1);
    SkASSERT(format.isValid() && format.backend() == fContext->backend());
    if (alphaType == kPremul_SkAlphaType || alphaType == kOpaque_SkAlphaType) {
        return skgpu::ganesh::SurfaceDrawContext::Make(this->context(), std::move(colorSpace), fit, dimensions, format,
            sampleCount, mipmapped, isProtected, readSwizzle, writeSwizzle, origin, budgeted, SkSurfaceProps(), label);
    }

    sk_sp<GrTextureProxy> proxy = this->proxyProvider()->createProxy(format, dimensions, GrRenderable::kYes,
        sampleCount, mipmapped, fit, budgeted, isProtected, label);
    if (!proxy) {
        return nullptr;
    }
    GrImageInfo info(GrColorType::kUnknown, alphaType, std::move(colorSpace), dimensions);
    GrSurfaceProxyView readView(proxy, origin, readSwizzle);
    GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);
    std::unique_ptr<skgpu::ganesh::SurfaceFillContext> sfc;
    sfc = std::make_unique<skgpu::ganesh::SurfaceFillContext>(this->context(), std::move(readView),
        std::move(writeView), info.colorInfo());
    sfc->discard();
    return sfc;
}

std::unique_ptr<skgpu::ganesh::SurfaceFillContext> GrRecordingContextPriv::makeSFCWithFallback(GrImageInfo info,
    SkBackingFit fit, int sampleCount, skgpu::Mipmapped mipmapped, GrProtected isProtected, GrSurfaceOrigin origin,
    skgpu::Budgeted budgeted)
{
    if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
        return skgpu::ganesh::SurfaceDrawContext::MakeWithFallback(this->context(), info.colorType(),
            info.refColorSpace(), fit, info.dimensions(), SkSurfaceProps(), sampleCount, mipmapped, isProtected, origin,
            budgeted);
    }
    const GrCaps *caps = this->caps();

    auto [ct, _] = caps->getFallbackColorTypeAndFormat(info.colorType(), sampleCount);
    if (ct == GrColorType::kUnknown) {
        return nullptr;
    }
    info = info.makeColorType(ct);
    return this->makeSFC(info, "MakeSurfaceContextWithFallback", fit, sampleCount, mipmapped, isProtected, origin,
        budgeted);
}

std::unique_ptr<skgpu::ganesh::SurfaceFillContext> GrRecordingContextPriv::makeSFCFromBackendTexture(GrColorInfo info,
    const GrBackendTexture &tex, int sampleCount, GrSurfaceOrigin origin, sk_sp<skgpu::RefCntedCallback> releaseHelper)
{
    SkASSERT(sampleCount > 0);

    if (info.alphaType() == kPremul_SkAlphaType || info.alphaType() == kOpaque_SkAlphaType) {
        return skgpu::ganesh::SurfaceDrawContext::MakeFromBackendTexture(this->context(), info.colorType(),
            info.refColorSpace(), tex, sampleCount, origin, SkSurfaceProps(), std::move(releaseHelper));
    }

    if (info.colorType() == GrColorType::kUnknown) {
        return nullptr;
    }

    const GrBackendFormat &format = tex.getBackendFormat();
    if (!this->caps()->areColorTypeAndFormatCompatible(info.colorType(), format)) {
        return nullptr;
    }
    skgpu::Swizzle readSwizzle = this->caps()->getReadSwizzle(format, info.colorType());
    skgpu::Swizzle writeSwizzle = this->caps()->getWriteSwizzle(format, info.colorType());

    sk_sp<GrTextureProxy> proxy(this->proxyProvider()->wrapRenderableBackendTexture(tex, sampleCount,
        kBorrow_GrWrapOwnership, GrWrapCacheable::kNo, std::move(releaseHelper)));
    if (!proxy) {
        return nullptr;
    }

    GrSurfaceProxyView readView(proxy, origin, readSwizzle);
    GrSurfaceProxyView writeView(std::move(proxy), origin, writeSwizzle);

    return std::make_unique<skgpu::ganesh::SurfaceFillContext>(this->context(), std::move(readView),
        std::move(writeView), std::move(info));
}
